#!/bin/bash

# Description:
#   Script to generate commit hashes from references generated by
#   `git rev-list` command.
#   Useful to recheck rev-list commits consistency.

# Usage:
#   git_gen_revlist_commit_hashes.sh [<flags>] [//] [<rev-list-cmd-line>] [// <show-cmd-line> [// [<hash-cmd> [<hash-cmd-line>]]]]
#
#   <flags>:
#     -c
#       Print only commits with not equal hashes.
#     -r
#       Execute `git replace --graft <commit> <parents>` for all not equal
#       hashes. Implies flag `-c`.
#   //:
#     Separator to stop parse flags or switch to next command line.
#   <rev-list-cmd-line>:
#     The command line passed to `git rev-list` command.
#   <show-cmd-line>:
#     The command line passed to `git show` command.
#     If not defined, then
#       `-s --format="Parents: %P"$'\n'"Author : %ai | %an <%ae> | %ar"$'\n'"Commit : %ci | %cn <%ce> | %cr"$'\n'"%B"`
#     is used.
#   <hash-cmd>:
#     The hash command to execute for stdin pipe.
#     If not defined, then `sha1sum` is used instead.
#   <hash-cmd-line>:
#     The hash command line for `<hash-cmd>` command.

# Examples:
#   >
#   cd myrepo/path
#   git_gen_revlist_commit_hashes.sh -c // master
#
#   >
#   cd myrepo/path
#   git_gen_revlist_commit_hashes.sh -c // master // -s // . -b
#
#   >
#   cd myrepo/path
#   git_gen_revlist_commit_hashes.sh -c // master // // git hash-object --stdin
#
#   >
#   cd myrepo/path
#   git_gen_revlist_commit_hashes.sh -r // master

# Script both for execution and inclusion.
[[ -n "$BASH" ]] || return 0 || exit 0 # exit to avoid continue if the return can not be called

function call()
{
  local IFS=$' \t'
  echo ">$*"
  "$@"
}

function git_gen_revlist_commit_hashes()
{
  local flag="$1"

  local flag_only_not_equal_hashes=0
  local flag_execute_replace_graft=0

  while [[ "${flag:0:1}" == '-' ]]; do
    flag="${flag:1}"

    if [[ "${flag:0:1}" == '-' ]]; then
      echo "$0: error: invalid flag: \`$flag\`" >&2
      return 255
    fi

    while [[ -n "$flag" ]]; do
      if [[ "${flag//c/}" != "$flag" ]]; then
        flag_only_not_equal_hashes=1
        flag="${flag//c/}"
      elif [[ "${flag//r/}" != "$flag" ]]; then
        flag_execute_replace_graft=1
        flag_only_not_equal_hashes=1
        flag="${flag//r/}"
      else
        echo "$0: error: invalid flag: \`${flag:0:1}\`" >&2
        return 255
      fi
    done

    shift

    flag="$1"
  done

  if [[ "$1" == '//' ]]; then
    shift
  fi

  local arg="$1"
  local revlist_cmdline

  revlist_cmdline=()

  while [[ -n "$arg" && "$arg" != '//' ]]; do
    revlist_cmdline=("${revlist_cmdline[@]}" "$arg")

    shift

    arg="$1"
  done

  if [[ "$1" == '//' ]]; then
    shift
  fi

  arg="$1"
  local show_cmdline

  show_cmdline=()

  while [[ -n "$arg" && "$arg" != '//' ]]; do
    show_cmdline=("${show_cmdline[@]}" "$arg")

    shift

    arg="$1"
  done

  if (( ! ${#show_cmdline[@]} )); then
    show_cmdline=(-s --format="Parents: %P"$'\n'"Author : %ai | %an <%ae> | %ar"$'\n'"Commit : %ci | %cn <%ce> | %cr"$'\n'"%B")
  fi

  if [[ "$1" == '//' ]]; then
    shift
  fi

  local hashcmd="$1"
  local hashcmdline=("${@:2}")

  if [[ -z "$hashcmd" || "$hashcmd" == '.' ]]; then
    hashcmd=("sha1sum")
  fi

  local line
  local hash ref

  local objtype refhash parenthash
  local hashsum
  local hashvalue suffix

  local print_empty_line num_parents

  local IFS

  IFS=$'\r\n'; for line in `git rev-list "${revlist_cmdline[@]}"`; do # IFS - with trim trailing line feeds
    print_empty_line=0

    IFS=$'\t ' read -r refhash suffix <<< "$line"

    objtype="$(git cat-file -t "$refhash")"
    hashsum="$(echo -ne "$objtype $(git cat-file -s "$refhash")\0$(git cat-file -p "$refhash")\n" | $hashcmd "${hashcmdline[@]}")"

    IFS=$'\t ' read -r hashvalue suffix <<< "$hashsum"

    if (( ! flag_only_not_equal_hashes )) || [[ "$hashvalue" != "$refhash" ]]; then
      echo "$hashvalue $refhash $objtype"
      echo "$(git show "${show_cmdline[@]}" "$refhash")"
      echo ---
      echo

      if (( flag_execute_replace_graft )) && [[ "$hashvalue" != "$refhash" ]]; then
        call git replace --graft "$refhash" $(git show -s --format="%P" "$refhash")
        echo ---
        echo
      fi
    fi
  done
}

# shortcut
function git_gen_rvl_c_hs()
{
  git_gen_revlist_commit_hashes "$@"
}

if [[ -z "$BASH_LINENO" || BASH_LINENO[0] -eq 0 ]]; then
  # Script was not included, then execute it.
  git_gen_revlist_commit_hashes "$@"
fi
